-
Notifications
You must be signed in to change notification settings - Fork 24.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(ivy): instantiate dirs in correct order #23178
Conversation
40bd9f8
to
7b1ec24
Compare
// If we *didn't* find the directive for the token and we are searching the current node's | ||
// injector, it's possible the directive is on this node and hasn't been created yet. | ||
let instance: T|null; | ||
if (injector === di && (instance = searchMatchesQueuedForCreation<T>(node, token))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this information get cached so that subsequent instantiations are fast? In other words, can we rearrange the order of directives until it is the correct order?
It looks to me that every time we want to inject parent directive we will go to searchMatchesQueuedForCreation
which will have negative perf impact. I think the logic should be something like
if (first instantiation) {
record the order in which the directives have been created (lookahead if needed)
update the current list of directives to the recorded list.
} else {
directives are in correct order just instantiate them.
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I think you mentioned in a later comment, this is the way it already works. The directives are saved in the correct order after the first template pass, so subsequent runs will just instantiate the directives in the right order.
It's worth noting that if you are injecting a parent directive, this search flow will never be entered (even in the first template pass) because DI would have retrieved the parent bloom injector and the injector === di
check would fail.
The only time the search occurs is:
- on first template pass
- if token's bit has been added to the bottom level bloom filter during directive matching (so we know it's in this list)
- AND the directive was not already created (out of order)
* If we visit a directive that has a value set to CIRCULAR, we know we've | ||
* already seen it, and thus have a circular dependency. | ||
*/ | ||
export const CIRCULAR = '__CIRCULAR__'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because it is a string someone could create it. Better to do {mark: 'circular'}
This way no one can got hold of that instance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The matches
array only contains directive defs, directive indices, and this constant, so there's no way user-provided values could be in there. I ended up using a string to avoid unnecessary object creation. Happy to change if you still think it's worth it.
node.tNode = tData[index] = tNode; | ||
node.tNode = tData[index] = createTNode(name, attrs || null, containerData); | ||
cacheMatchingDirectivesForNode(node.tNode, currentView.tView, localRefs || null); | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ohh I see, I think this is where the caching happens. So my previous comment may not be applicable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, directives are saved in the correct order for instantiation after the first run.
You can preview 235612e at https://pr23178-235612e.ngbuilds.io/. |
/** <div dirA dirB dirC></div> */ | ||
const App = createComponent('app', function(ctx: any, cm: boolean) { | ||
if (cm) { | ||
elementStart(0, 'div', ['dirA', '', 'dirB', '', 'dirC', 'dirC']); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'dirC', 'dirC'
-> 'dirC', ''
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! It shouldn't affect the test, but it doesn't match the comment, so I've updated the PR.
You can preview e855190 at https://pr23178-e855190.ngbuilds.io/. |
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Currently, directives have to be listed in directive defs in the correct order or injected directives will not be found. For example, consider the following setup, where
DirA
injectsDirB
, butDirA
is created first.In this case,
DirB
won't be found by DI because it hasn't been created yet.This PR allows DI to eagerly instantiate a directive dependency if we know it's been defined on the same node, but not yet created. The extra step will only occur on first template pass because directives are now saved to
tView.directives
in the correct instantiation order.It also adds logic to throw a helpful error if there's a circular dependency.
Notes:
currentMatches
has been added to theTView
as a temporary holder for selector matches on the node while injection occurs. This allows DI to check whether a directive is queued to be created on the same node and if so, use the def provided to instantiate the injected directive first.directiveInject
because it would require an expensive megamorphic property access to get the selector (token.ngDirectiveDef.selector
).factory
fn directly, but this would muddy up the API and add extra bytes to the generated code.